/*
	pattern.cc	Pattern database
	Copyright (c) 2001, 2002, 2003 Kriang Lerdsuwanakij
	email:		lerdsuwa@users.sourceforge.net

	This program is free software; you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation; either version 2 of the License, or
	(at your option) any later version.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with this program; if not, write to the Free Software
	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

#include "pattern.h"
#include "order.h"
#include "gtstream.h"

#include <cstring>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <stdexcept>
#include <windows.h>

#ifdef _
#undef _
#endif

#ifdef N_
#undef N_
#endif

#include <libintl.h>
#define _(x) gettext(x)
#define N_(x) (x)

#define pattern_path1 "../share/grhino-0.16.1/pattern/"


// These are not correct in terms of symmetry but provide better
// opening moves

const char row1_pattern[4][8] = {
	{ A1, B1, C1, D1, E1, F1, G1, H1 },
	{ A8, B8, C8, D8, E8, F8, G8, H8 },
	{ A1, A2, A3, A4, A5, A6, A7, A8 },
	{ H1, H2, H3, H4, H5, H6, H7, H8 }
};

const char row2_pattern[4][8] = {
	{ A2, B2, C2, D2, E2, F2, G2, H2 },
	{ A7, B7, C7, D7, E7, F7, G7, H7 },
	{ B1, B2, B3, B4, B5, B6, B7, B8 },
	{ G1, G2, G3, G4, G5, G6, G7, G8 }
};

const char row3_pattern[4][8] = {
	{ A3, B3, C3, D3, E3, F3, G3, H3 },
	{ A6, B6, C6, D6, E6, F6, G6, H6 },
	{ C1, C2, C3, C4, C5, C6, C7, C8 },
	{ F1, F2, F3, F4, F5, F6, F7, F8 }
};

const char row4_pattern[4][8] = {
	{ A4, B4, C4, D4, E4, F4, G4, H4 },
	{ A5, B5, C5, D5, E5, F5, G5, H5 },
	{ D1, D2, D3, D4, D5, D6, D7, D8 },
	{ E1, E2, E3, E4, E5, E6, E7, E8 }
};

const char diag1_pattern[2][8] = {
	{ A1, B2, C3, D4, E5, F6, G7, H8 },
	{ H1, G2, F3, E4, D5, C6, B7, A8 }
};

const char diag2_pattern[4][7] = {
	{ A2, B3, C4, D5, E6, F7, G8 },
	{ B1, C2, D3, E4, F5, G6, H7 },
	{ H2, G3, F4, E5, D6, C7, B8 },
	{ G1, F2, E3, D4, C5, B6, A7 }
};

const char diag3_pattern[4][6] = {
	{ A3, B4, C5, D6, E7, F8 },
	{ C1, D2, E3, F4, G5, H6 },
	{ H3, G4, F5, E6, D7, C8 },
	{ F1, E2, D3, C4, B5, A6 }
};

const char diag4_pattern[4][5] = {
	{ A4, B5, C6, D7, E8 },
	{ D1, E2, F3, G4, H5 },
	{ H4, G5, F6, E7, D8 },
	{ E1, D2, C3, B4, A5 }
};

const char diag5_pattern[4][4] = {
	{ A5, B6, C7, D8 },
	{ E1, F2, G3, H4 },
	{ H5, G6, F7, E8 },
	{ D1, C2, B3, A4 }
};

const char edge_x_pattern[4][10] = {
	{ B2, A1, B1, C1, D1, E1, F1, G1, H1, G2 },
	{ B7, A8, B8, C8, D8, E8, F8, G8, H8, G7 },
	{ B2, A1, A2, A3, A4, A5, A6, A7, A8, B7 },
	{ G2, H1, H2, H3, H4, H5, H6, H7, H8, G7 }
};

const char corner5x2_pattern[8][10] = {
	{ A1, B1, C1, D1, E1, A2, B2, C2, D2, E2 },
	{ H1, G1, F1, E1, D1, H2, G2, F2, E2, D2 },
	{ A8, B8, C8, D8, E8, A7, B7, C7, D7, E7 },
	{ H8, G8, F8, E8, D8, H7, G7, F7, E7, D7 },
	{ A1, A2, A3, A4, A5, B1, B2, B3, B4, B5 },
	{ A8, A7, A6, A5, A4, B8, B7, B6, B5, B4 },
	{ H1, H2, H3, H4, H5, G1, H2, H3, H4, G5 },
	{ H8, H7, H6, H5, H4, G8, H7, H6, H5, G4 }
};

const pattern_data_t pattern_data[PATTERN_UNKNOWN] = {
	{ 8, true, 4, pattern_path1 "row1.pat", pattern_path1 "row1.bin",
	  { row1_pattern[0], row1_pattern[1],
	    row1_pattern[2], row1_pattern[3], 0, 0, 0, 0 } },
	{ 8, true, 4, pattern_path1 "row2.pat", pattern_path1 "row2.bin",
	  { row2_pattern[0], row2_pattern[1],
	    row2_pattern[2], row2_pattern[3], 0, 0, 0, 0 } },
	{ 8, true, 4, pattern_path1 "row3.pat", pattern_path1 "row3.bin",
	  { row3_pattern[0], row3_pattern[1],
	    row3_pattern[2], row3_pattern[3], 0, 0, 0, 0 } },
	{ 8, true, 4, pattern_path1 "row4.pat", pattern_path1 "row4.bin",
	  { row4_pattern[0], row4_pattern[1],
	    row4_pattern[2], row4_pattern[3], 0, 0, 0, 0 } },
	{ 8, true, 2, pattern_path1 "diag1.pat", pattern_path1 "diag1.bin",
	  { diag1_pattern[0], diag1_pattern[1],
	    0, 0, 0, 0, 0, 0 } },
	{ 7, true, 4, pattern_path1 "diag2.pat", pattern_path1 "diag2.bin",
	  { diag2_pattern[0], diag2_pattern[1],
	    diag2_pattern[2], diag2_pattern[3], 0, 0, 0, 0 } },
	{ 6, true, 4, pattern_path1 "diag3.pat", pattern_path1 "diag3.bin",
	  { diag3_pattern[0], diag3_pattern[1],
	    diag3_pattern[2], diag3_pattern[3], 0, 0, 0, 0 } },
	{ 5, true, 4, pattern_path1 "diag4.pat", pattern_path1 "diag4.bin",
	  { diag4_pattern[0], diag4_pattern[1],
	    diag4_pattern[2], diag4_pattern[3], 0, 0, 0, 0 } },
	{ 4, true, 4, pattern_path1 "diag5.pat", pattern_path1 "diag5.bin",
	  { diag5_pattern[0], diag5_pattern[1],
	    diag5_pattern[2], diag5_pattern[3], 0, 0, 0, 0 } },
	{ 10, true, 4, pattern_path1 "edge-x.pat", pattern_path1 "edge-x.bin",
	  { edge_x_pattern[0], edge_x_pattern[1],
	    edge_x_pattern[2], edge_x_pattern[3], 0, 0, 0, 0 } },
	{ 10, false, 8, pattern_path1 "corner5x2.pat", pattern_path1 "corner5x2.bin",
	  { corner5x2_pattern[0], corner5x2_pattern[1],
	    corner5x2_pattern[2], corner5x2_pattern[3],
	    corner5x2_pattern[4], corner5x2_pattern[5],
	    corner5x2_pattern[6], corner5x2_pattern[7] } }
};

pattern_info	*pattern_table[PATTERN_UNKNOWN][num_move_index];

// File used by gen_pattern
const char *get_pattern_file_public(pattern_t p)
{
	return pattern_data[p].pattern_file;
}

// File used by gen_pattern
const char *get_pattern_file_private(pattern_t p)
{
	const char * s = get_pattern_file_public(p);
	const char * l = strrchr(s, '/');
	if (l)
		s = l+1;
	return s;
}

// File containing condensed information for GRhino
const char *get_pattern_data_file_public(pattern_t p)
{
	return pattern_data[p].data_file;
}

// File containing condensed information for GRhino
const char *get_pattern_data_file_private(pattern_t p)
{
	const char * s = get_pattern_data_file_public(p);
	const char * l = strrchr(s, '/');
	if (l)
		s = l+1;
	return s;
}

const char *get_pattern_file(pattern_t p)
{
	if (get_use_private_files())
		return get_pattern_file_private(p);
	else
		return get_pattern_file_public(p);
}

const char *get_pattern_data_file(pattern_t p)
{
	if (get_use_private_files())
		return get_pattern_data_file_private(p);
	else
		return get_pattern_data_file_public(p);
}

void	pattern_table_init(pattern_t p)
{
	int size = pow_3[get_pattern_piece(p)];
	int comp_size = size/3*2;

	int handle = open(get_pattern_data_file(p), O_RDONLY | O_BINARY);
	if (handle == -1) {
		gtstream bufstr;
		gtout(bufstr, _("cannot open file %$\n"))
			<< get_pattern_data_file(p);
		throw std::runtime_error(bufstr.str());
	}
	for (int i = 0; i < num_move_index; ++i) {
		pattern_table[p][i] = new pattern_info[size];
		if (read(handle, pattern_table[p][i], comp_size) != comp_size) {
			close(handle);

			gtstream bufstr;
			
			gtout(bufstr, _("cannot read file %$\n"))
				<< get_pattern_data_file(p);
			throw std::runtime_error(bufstr.str());
		}
		for (int j = comp_size, k = size/3-1; j < size; ++j, --k)
			pattern_table[p][i][j] = -pattern_table[p][i][k];
	}
	close(handle);
}

void	pattern_table_init()
{
	pattern_table_init(PATTERN_ROW1);
	pattern_table_init(PATTERN_ROW2);
	pattern_table_init(PATTERN_ROW3);
	pattern_table_init(PATTERN_ROW4);
	pattern_table_init(PATTERN_DIAG1);
	pattern_table_init(PATTERN_DIAG2);
	pattern_table_init(PATTERN_DIAG3);
	pattern_table_init(PATTERN_DIAG4);
	pattern_table_init(PATTERN_DIAG5);
	pattern_table_init(PATTERN_CORNER5X2);
}

inline int	pattern_eval(byte_board_info *board, int pos, pattern_t p)
{
	int	score = 0;
	for (int i = 0; i < pattern_data[p].num_pattern; ++i) {
		int pos1 = 0;
		for (int j = 0; j < pattern_data[p].piece; ++j) {
			pos1 += pow_3[j] * to_pattern_index(board->board[
						static_cast<int>(pattern_data[p].pattern[i][j])]);
		}
		score += pattern_table[p][pos][pos1];
	}
	return score;
}

int	pattern_eval(byte_board_info *board, pattern_t p, int index)
{
	if (board->get_num_move() == 0)
		return 0;		// Beginning of the game

	int pos = to_move_index(board->get_num_move());
	int pos1 = 0;
	for (int j = 0; j < pattern_data[p].piece; ++j) {
		pos1 += pow_3[j] * to_pattern_index(board->board[
						static_cast<int>(pattern_data[p].pattern[index][j])]);
	}
	return pattern_table[p][pos][pos1];
}

int	pattern_eval(byte_board_info *board)
{
	if (board->get_num_move() == 0)
		return 0;		// Beginning of the game

	int score = 0;
	int pos = to_move_index(board->get_num_move());
	score += pattern_eval(board, pos, PATTERN_ROW1);
	score += pattern_eval(board, pos, PATTERN_ROW2);
	score += pattern_eval(board, pos, PATTERN_ROW3);
	score += pattern_eval(board, pos, PATTERN_ROW4);
	score += pattern_eval(board, pos, PATTERN_DIAG1);
	score += pattern_eval(board, pos, PATTERN_DIAG2);
	score += pattern_eval(board, pos, PATTERN_DIAG3);
	score += pattern_eval(board, pos, PATTERN_DIAG4);
	score += pattern_eval(board, pos, PATTERN_DIAG5);
	score += pattern_eval(board, pos, PATTERN_CORNER5X2);
	return score;
}

inline void	pattern_eval_debug(byte_board_info *board, int pos, pattern_t p)
{
	int	score = 0;
	std::cout << "                Pat" << int(p) << ' ';
	for (int i = 0; i < pattern_data[p].num_pattern; ++i) {
		int pos1 = 0;
		for (int j = 0; j < pattern_data[p].piece; ++j) {
			switch(board->board[static_cast<int>(pattern_data[p].pattern[i][j])]) {
				case BLACK:
					std::cout << "X ";
					break;
				case WHITE:
					std::cout << "O ";
					break;
				case EMPTY:
					std::cout << ". ";
					break;
			}
			pos1 += pow_3[j] * to_pattern_index(board->board[
						static_cast<int>(pattern_data[p].pattern[i][j])]);
		}
		std::cout << "-> " << int(pattern_table[p][pos][pos1]) << '\n';
		score += pattern_table[p][pos][pos1];
	}
}

void	pattern_eval_debug(byte_board_info *board)
{
	int pos = to_move_index(board->get_num_move());
	pattern_eval_debug(board, pos, PATTERN_ROW1);
	pattern_eval_debug(board, pos, PATTERN_ROW2);
	pattern_eval_debug(board, pos, PATTERN_ROW3);
	pattern_eval_debug(board, pos, PATTERN_ROW4);
	pattern_eval_debug(board, pos, PATTERN_DIAG1);
	pattern_eval_debug(board, pos, PATTERN_DIAG2);
	pattern_eval_debug(board, pos, PATTERN_DIAG3);
	pattern_eval_debug(board, pos, PATTERN_DIAG4);
	pattern_eval_debug(board, pos, PATTERN_DIAG5);
	pattern_eval_debug(board, pos, PATTERN_CORNER5X2);
}

void	show_pattern(pattern_t p, int i)
{
	std::cout << " ";
	for (int j = 0; j < get_pattern_piece(p); ++j) {
		switch (to_board_index(i % 3)) {
			case WHITE:
				std::cout << "O ";
				break;
			case BLACK:
				std::cout << "X ";
				break;
			case EMPTY:
				std::cout << ". ";
				break;
		}
		i /= 3;
	}
}

// pos = 1 .. 60
int	to_move_index(int pos)
{
	return (pos - 1)/4;
}

